// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

/* Shows how to use both command line and config file. */
#include "stdafx.h"

#include "engine.hpp"
#include "token.hpp"
#include "encrypt_aes.hpp"
#include "config.hpp"
#ifdef WIN32
#include <input_interface.hpp>
#else
#include "../input/input_interface.hpp"
#endif


void engine::write(const char* _token)
{
	token inner_tk(_config.get_max_inner_token_len(), _config.get_inner_token_min_distr_value(), 
				   _config.get_inner_token_max_distr_value(), _config.get_seed_file());
	std::string inner_s(inner_tk.experiment());

	std::string s;
	if(_token == NULL)
	{
		token tk(_config.get_max_token_len(), _config.get_min_distr_value(), _config.get_max_distr_value(), 
			_config.get_allowed_punctuation(), _config.get_forbidden_punctuation(), _config.get_first_char_as_punct(), 
			_config.get_seed_file());

		s = tk.experiment();
	}
	else
	 s = _token;
/*
#ifdef _DEBUG
	std::cout << "token is: " << s << std::endl;
#endif
	*/

if(!(_config.get_cipher_file().empty() && _config.get_key_file().empty() && _config.get_iv_file().empty()))
  {
    write('\0', false); // backup before generating new token
 	 encrypt_aes::encrypt_into(s.c_str(), inner_s.c_str(), _config.get_cipher_file(), 
								  _config.get_key_file(), _config.get_iv_file());
	}  
}


#include <fstream>
#include <boost/lexical_cast.hpp>
#include "pdfwrapper.hpp"

void engine::write(const char* _file_name, bool _just_one_char_per_page)
{
	std::vector<unsigned short> null;
	write(_file_name, null, _just_one_char_per_page);
}

#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>

using namespace boost::posix_time;
using namespace std;


void engine::write(const char* _filename, std::vector<unsigned short> const& _substring_indices, bool _just_one_char_per_page)
{
	//if(_filename != NULL)
	//{
		std::string	s = encrypt_aes::decrypt_from(_config.get_cipher_file(), _config.get_key_file()
									  ,_config.get_iv_file(), _substring_indices);
				
    if(!s.empty())
    {
        std::string basename;
        if(_filename != NULL)
        {
          basename = get_basename(_filename));
        }
        else
        {
           basename = get_default_exportfile();
        }
        basename += ".pdf";

//        pdfwrapper pdf;
			const char* word = s.c_str();	
			pdfwrapper::write(filename, word,_just_one_char_per_page);	
			std::cout << "backup into file = " << filename << std::endl;
    }
	//}
}


const std::string engine::get_default_exportfile() const
{
	std::string export_filename = _config.get_export_filename();

	if(export_filename.empty())
		export_filename = config::DEFAULT_EXPORT_FILENAME + _config.get_config_filename();

	std::string basename = engine::get_basename(export_filename.c_str());

	// we have to find out a folder to write into
	// default folder = %HOMEPATH% in windows; and $HOME in linux/unix

	std::string home_path;
#ifdef WIN32
	home_path = config::get_env_var("HOMEDRIVE");
	home_path += config::get_env_var("HOMEPATH");
	home_path += "\\";
#else
	home_path = config::get_env_var("HOME");
	home_path = home_path + "/";
#endif
	
	export_filename = home_path + basename;
	return export_filename;
}


const std::string engine::get_basename(const char* _filename, bool _add_timestamp)
{

	std::string basename = _filename;
	std::string suffix("");
#ifdef _DEBUG
		std::cout << "basename = " << basename << std::endl;
#endif
	std::string::size_type idx = basename.rfind('.');
	if(idx != std::string::npos)
	{
		suffix = basename.substr(idx); // '.' is included
		basename = basename.substr(0,idx);
#ifdef _DEBUG
		std::cout << "suffix = "<< suffix << std::endl;
		std::cout << "basename = " << basename << std::endl;
#endif				
	}

	if(_add_timestamp)
	{
		std::string dt = boost::posix_time::to_simple_string(second_clock::local_time());
		for(unsigned short j=0; j< dt.length(); j++)
		{
			switch(dt[j])
			{
			case ':': dt[j]='.'; break;
			case ' ': dt[j] = '_'; break;
			default: break;
			}
		}
		basename += '_'+dt;
	}

	return basename;
}

std::string const engine::read(unsigned short* _index_ptr)
{
	std::vector<unsigned short> null;
	std::string token;

	if(!_config.get_keypad_config_filename().empty()) // further processing for ingdirect
	{
		try
		{
			ingdirect_tk ing_tk(_config.get_keypad_config_filename().c_str());
			ingdirect_it ing_cf = ing_tk.get_ingdirect_conf();

			unsigned short sz(0);
			std::cout << ing_cf << std::endl;

			if((sz = ing_cf.get_ingdirect_pinpad_coords().size() ) == 10) // check if we have stored all ten coordinates in config file
			{
				std::string pinpad_sequence(ingdirect_tk::get_pinpad_sequence()); // get actual pinpad sequence
				std::string pin_layout(ingdirect_tk::get_pin_layout()); // get requested slots

				ingdirect_tk::wait_ready_state_interactive(); // wait for usb stick to be inserted

				// once we have all information about pinpad and digits, we can read the whole pin to return a subset
				std::vector<unsigned short> null;
				std::string pin = encrypt_aes::decrypt_from(_config.get_cipher_file(), _config.get_key_file(), _config.get_iv_file()
							 , null);	
				if(!pin.empty())
					token = ing_tk.set_ingdirect_pinpad_coordinates(pin, pinpad_sequence, pin_layout, _index_ptr);
			}
			else
				throw ("ten ingdirect pinpad coordinates are needed: check regular expression or the string to extract them from");
		}
		catch(const char* e)
		{
			std::string null;
			std::cout << e << std::endl;

			return null;
		}
	}
	else
	{
		token = encrypt_aes::decrypt_from(_config.get_cipher_file(), _config.get_key_file(), _config.get_iv_file()
							 , null);
	}

	return token;
}

std::string const engine::read(std::vector<unsigned short> const& _substring_indices)
{
	return encrypt_aes::decrypt_from(_config.get_cipher_file(), _config.get_key_file(), _config.get_iv_file()
							 ,_substring_indices);
}


engine::engine(const char* _filename): _config(_filename)
{ }

config const engine::get_config() const
{
	return _config;
}


std::string const engine::get_keypad_config_filename() const
{
	return _config.get_keypad_config_filename();
}

engine& engine::operator=(engine const& _tk)
{
	_config = _tk._config;

	return *this;
}

ingdirect_tk::ingdirect_tk(const char* ingdirect_filename): _ingdirect(ingdirect_filename)
{
};



std::string const ingdirect_tk::get_pinpad_sequence()
{
	libinput input_interface;
	std::string pinpad_layout = input_interface.getInput()->get_pinpad_sequence();

	return pinpad_layout;
}

std::string const ingdirect_tk::get_pin_layout()
{
	libinput input_interface;
	std::string pin_layout = input_interface.getInput()->get_pin_sequence();

	return pin_layout;
}


const std::string ingdirect_tk::set_ingdirect_pinpad_coordinates(std::string const& pin, 
		 std::string const &pinpad_layout, std::string const& pin_layout, unsigned short* _index_ptr)
{
		
	std::string pinpad_coords;

	unsigned short pin_layout_len = pin_layout.length(),
		pinpad_layout_len = pinpad_layout.length();
	

	for(unsigned k=0, l=0; k< pin_layout_len 
		&& pin_layout_len <= pinpad_layout_len; k++)
	{
		
		unsigned short idx = boost::lexical_cast<unsigned short>(pin_layout[k]);	
		if(idx <= 0)
		{
			cerr << ">Controllare l'ordine delle cifre del PIN" << std::endl;
			break;
		}

		unsigned short j = pinpad_layout.find(pin[idx-1]);

		if(!pinpad_coords.empty())
		{
			pinpad_coords += _ingdirect.get_ingdirect_pinpad_coords_sep();
		}

		pinpad_coords += _ingdirect.get_ingdirect_pinpad_coords()[j];		

		if(_index_ptr != NULL)
		{
			_index_ptr[l++] = j; 
		}
	}

	return pinpad_coords;
}

ingdirect_it const& ingdirect_tk::get_ingdirect_conf()
{
	return _ingdirect;
}


bool ingdirect_tk::wait_ready_state_interactive()
{
	libinput input_interface;
	return input_interface.getInput()->wait_ready_state_token();
}
